using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Rhizome.Contract;
using Rhizome.Contract.Diag;
using Rhizome.Model.Validation;

namespace Rhizome.Model
{
	public sealed class TimerTrigger : TriggerBase<object>, IForwardEvent
	{
		private Timer _timer;
		private static readonly TimeSpan Minus1Ms = TimeSpan.FromMilliseconds(-1);

		/// <summary>
		/// The System.TimeSpan representing the amount of time to delay before trigger will be trigged for the first time. 
		/// Specify zero (0) to start the timer immediately.
		/// Specify negative one (-1) millisecond {-00:00:00.0010000} to use Period value insted of DueTime
		/// Default is (-1) millisecond
		/// </summary>
		public TimeSpan DueTime { get; set; }

		/// <summary>
		/// The time interval between trigger will be trigged
		/// Specify negative one (-1) millisecond {-00:00:00.0010000} to disable periodic signaling.
		/// Default is (-1) millisecond
		/// </summary>
		public TimeSpan Period { get; set; }

		public TimerTrigger()
		{
			Period = Minus1Ms;
			DueTime = Minus1Ms;
		}

		#region Overrides of RuleBase<object>

		public override void Initialize()
		{
			if (_timer != null) throw new InvalidOperationException("Timer must be null before start");

			if (DueTime == Minus1Ms && Period <= TimeSpan.Zero) return;
			
			_timer = 
				new Timer(
					state => Trigged(), null, 
					DueTime ==  Minus1Ms ? Period : DueTime, 
					Period);
		}

		public override void Cleanup()
		{
			if (_timer != null)
			{
				_timer.Dispose();
				_timer = null;
			}
		}

		#endregion

		#region Validation

		public override IEnumerable<IValidationResult> GetValidationResults()
		{
			return base.GetValidationResults().Concat(InternalGetValidationResults());
		}

		private IEnumerable<IValidationResult> InternalGetValidationResults()
		{
			if (DueTime != Minus1Ms && DueTime <= TimeSpan.Zero)
				yield return ValidationResult.Error("DueTime must be >= 0 msec or {-00:00:00.0010000}");

			if (Period != Minus1Ms && Period <= TimeSpan.Zero)
				yield return ValidationResult.Error("Period must be >= 0 msec or = {-00:00:00.0010000}");

			if (DueTime == Minus1Ms && Period <= TimeSpan.Zero)
				yield return ValidationResult.Warning("Timer will not be hit");
		}

		#endregion

		#region Implementation of IForwardEvent

		public override object GetForwardEvent()
		{
			return DueTime == TimeSpan.Zero ? base.GetForwardEvent() : null;
		}

		#endregion
	}
}